# parser for graphviz dot plain graph format
# generate this data with the -Tplain options of dot
# the parsed data can be used to generate other data
# the parsed data can be used for own graph layout algorithms
#
# The plain and plain-ext formats produce output using a simple,
# line-based language.
# The latter format differs in that, on edges, it provides port names
# on head and tail nodes when applicable.
# There are four types of statements.
# graph scale width height
# node name x y width height label style shape color fillcolor
# edge tail head n x₁ y₁ .. xₙ yₙ [label xl yl] style color
# stop

%prefix "gvplain"

%source{

#include <stdio.h>

static const char *dbg_str[] = { "Evaluating rule", "Matched rule", "Abandoning rule" };

#define PCC_DEBUG(event, rule, level, pos, buffer, length) \
    fprintf(stdout, "%*s%s %s @%d [%.*s]\n", (int)(level * 2), "", dbg_str[event], rule, (int)pos, (int)length,  buffer); fflush(stdout)

    /* NOTE: To guarantee the output order, stderr, which can lead a race condition with stdout, is not used. */

}

gvplain	<- lines _ !.

lines	<- linegraph nodes* edges* linestop*

linegraph	<- 'graph' ' ' scale ' ' width ' ' height _

scale	<- fpnum

width	<- fpnum

height	<- fpnum

nodes	<- 'node' ' ' node_name ' ' node_x ' ' node_y ' ' node_width ' ' node_height ' ' node_label ' ' node_style ' ' node_shape ' ' node_color ' ' node_fillcolor _  { printf("node: \"%s\"\n",$0); }

node_name	<- '"' node_name_char* '"'
		/(!' ' .)*

node_name_char	<-
	'\\' "\""
	/ '\\' '\\'
	/ '\\' 'b'
	/ '\\' 'f'
	/ '\\' 'n'
	/ '\\' 'r'
	/ '\\' 't'
	/ (!"\"" .)

node_x	<- fpnum

node_y	<- fpnum

node_width	<- fpnum

node_height	<- fpnum

node_label	<- '"'  node_char* '"'
		/ (!' ' .)*

node_char	<-
	'\\' "\""
	/ '\\' '\\'
	/ '\\' 'b'
	/ '\\' 'f'
	/ '\\' 'n'
	/ '\\' 'r'
	/ '\\' 't'
	/ (!"\"" .)

node_style	<- (!' ' .)*

node_shape	<- (!' ' .)*

node_color	<- (!' ' .)*

node_fillcolor	<- (![\n] .)*

edges	<- (edges_el / edges_wl)

edges_el	<- 'edge' ' ' edge_tail _ edge_head ' ' edge_n ' ' edge_pair+ edge_label ' ' edge_xl ' ' edge_yl ' ' edge_style ' ' edge_color _ { printf("edge-with-edgelabel: \"%s\"\n",$0); }

edges_wl	<- 'edge' ' ' edge_tail _ edge_head ' ' edge_n ' ' edge_pair+ edge_style ' ' edge_color _  { printf("edge-without-edgelabel: \"%s\"\n",$0); }

edge_tail	<- '"' edge_tail_char* '"'
		/(!' ' .)*

edge_tail_char	<-
	'\\' "\""
	/ '\\' '\\'
	/ '\\' 'b'
	/ '\\' 'f'
	/ '\\' 'n'
	/ '\\' 'r'
	/ '\\' 't'
	/ (!"\"" .)

edge_head	<- '"' edge_head_char* '"'
		/ (!' ' .)*

edge_head_char	<-
	'\\' "\""
	/ '\\' '\\'
	/ '\\' 'b'
	/ '\\' 'f'
	/ '\\' 'n'
	/ '\\' 'r'
	/ '\\' 't'
	/ (!"\"" .)

edge_pair	<- edge_x ' ' edge_y ' '

edge_n	<- (!' ' .)*

edge_x	<- fpnum

edge_y	<- fpnum

edge_style <- (!' ' .)*

edge_color	<- (!eol .)*

edge_label	<- '"' edge_char* '"'
		/ (!' ' .)*

edge_char	<-
	'\\' "\""
	/ '\\' '\\'
	/ '\\' 'b'
	/ '\\' 'f'
	/ '\\' 'n'
	/ '\\' 'r'
	/ '\\' 't'
	/ (!"\"" .)

edge_xl	<- fpnum

edge_yl	<- fpnum

linestop	<- 'stop'

fpnum	<-	digit+ ('.' digit*)?

digit	<-	[0-9]

eol	<-	[\n]

_	<-	[ \t\n\r]*

%%

int main() {
    gvplain_context_t *ctx=gvplain_create(NULL);
    while (gvplain_parse(ctx, NULL)){;}
    gvplain_destroy(ctx);
    return 0;
}
